package es

import (
	"bytes"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	"github.com/elastic/go-elasticsearch/v7"
	"github.com/elastic/go-elasticsearch/v7/esapi"
	"github.com/tidwall/gjson"
	"io/ioutil"
)

type Config struct {
	// 多个地址客户端会自动随机访问 ，格式如
	//Addresses: []string{
	//    "http://localhost:9200",
	//    "http://localhost:9201",
	//},
	Addresses []string
	Username  string
	Password  string
}

type Es struct {
	esHandle *elasticsearch.Client
}

type Response struct {
	rawBody string
	errMsg  string
}

var GConfig Config

// InitConfig 初始化es配置
func InitConfig(config Config) {
	GConfig = config
}

// NewClient 使用全局配置实例化
func NewClient() *Es {
	return NewClientWithConfig(GConfig)
}

// NewClientWithConfig 带config实例化
func NewClientWithConfig(config Config) *Es {
	cfg := elasticsearch.Config{
		Addresses: config.Addresses,
		Username:  config.Username,
		Password:  config.Password,
	}
	es := Es{}
	var err error
	es.esHandle, err = elasticsearch.NewClient(cfg)
	if err != nil {
		logs.Error("es new client error", err)
	}
	return &es
}

// Search 搜索
// example:
//cli.Search("test", ghelp.JsonEncode(ghelp.MAP{
//    "query": ghelp.MAP{
//         "match_all": ghelp.MAP{},
//     },
//}))
func (e *Es) Search(index string, body string) Response {
	buf := bytes.NewBufferString(body)
	res, err := e.esHandle.Search(
		e.esHandle.Search.WithIndex(index),
		e.esHandle.Search.WithBody(buf),
	)
	if err != nil {
		return newResponse(err.Error(), "")
	}
	defer res.Body.Close()
	return e.response(res)
}

func (e *Es) response(res *esapi.Response) Response {
	body, _ := ioutil.ReadAll(res.Body)
	strBody := string(body)
	if res.IsError() {
		return newResponse(
			fmt.Sprintf("%s:%s", res.Status(), gjson.Get(strBody, "error.reason")),
			strBody,
		)
	}
	return newResponse("", strBody)
}

// Insert 写入数据
// index 索引名称，id 数据id不允许重复，body 写入的数据
// example:
//cli.Insert("test", "123", ghelp.JsonEncode(ghelp.MAP{
//    "name": "sven",
//    "age": 18,
//}))
func (e *Es) Insert(index, id, body string) Response {
	buf := bytes.NewBufferString(body)
	res, err := e.esHandle.Create(index, id, buf)
	if err != nil {
		return newResponse(err.Error(), "")
	}
	defer res.Body.Close()
	return e.response(res)
}

// Update 更新数据
// index 索引名称，id 数据id，body 更新的数据
// example:
//cli.Insert("test", "123", ghelp.JsonEncode(ghelp.MAP{
//   "doc": ghelp.MAP{
// 		 "name": "sven",
//       "age": 18,
//   }
//}))
func (e *Es) Update(index, id, body string) Response {
	buf := bytes.NewBufferString(body)
	res, err := e.esHandle.Update(index, id, buf)
	if err != nil {
		return newResponse(err.Error(), "")
	}
	defer res.Body.Close()
	return e.response(res)
}

// Delete 删除数据
// index 索引名称，id 数据id
func (e *Es) Delete(index, id string) Response {
	res, err := e.esHandle.Delete(index, id)
	if err != nil {
		return newResponse(err.Error(), "")
	}
	defer res.Body.Close()
	return e.response(res)
}

// Bulk 批量操作
// 每行json后面必须要加\n, 如下
// {"index":{"_index":"indexName","_type":"_doc","_id":"123456"}}\n{"title":"sven","age":18}
func (e *Es) Bulk(body string) Response {
	buf := bytes.NewBufferString(body)
	res, err := e.esHandle.Bulk(buf)
	if err != nil {
		return newResponse(err.Error(), "")
	}
	defer res.Body.Close()
	return e.response(res)
}

func newResponse(errMsg, body string) Response {
	return Response{
		rawBody: body,
		errMsg:  errMsg,
	}
}

// IsSuccess 返回是否成功
func (r *Response) IsSuccess() bool {
	if r.errMsg == "" {
		return true
	} else {
		return false
	}
}

// GetErrMsg 获取错误数据
func (r *Response) GetErrMsg() string {
	return r.errMsg
}

// GetBody 获取Body数据
func (r *Response) GetBody() gjson.Result {
	return gjson.Parse(r.rawBody)
}
